package top.lishuoboy.springmvc.a9;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import lombok.NonNull;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.UUID;

/**
 * 九、文件上传和下载
 */
@Controller
@RequestMapping("/a9")
@Slf4j
public class FileUploadDownloadController {

    /**
     * 首页
     * http://localhost:8080/lishuoboy_springmvc_war_exploded/a9
     */
    @GetMapping("")
    private String index() {
        return "a9/index";
    }

    /**
     * 1、文件下载
     * 使用ResponseEntity实现下载文件的功能
     * http://localhost:8080/lishuoboy_springmvc_war_exploded/a9/download?fileName=logback-test.xml&dirType=webapp
     * http://localhost:8080/lishuoboy_springmvc_war_exploded/a9/download?fileName=logback-test.xml&dirType=resource
     */
    @SneakyThrows
    @GetMapping("/download")
    private ResponseEntity<byte[]> fileDownload(String fileName, @NonNull String dirType, HttpSession session) {
        //获取ServletContext对象
        ServletContext servletContext = session.getServletContext();
        //获取服务器中文件的真实路径
        String realPath = servletContext.getRealPath("/WEB-INF/classes/" + fileName);
        String resourcePath = ResourceUtil.getResource(fileName).getFile();
        String filePath = null;
        if (dirType.equalsIgnoreCase("webapp")) {
            filePath = realPath;
        } else if (dirType.equalsIgnoreCase("resource")) {
            filePath = resourcePath;
        } else {
            //例如： 绝对路径
        }
        log.warn("realPath==    {}", realPath);
        log.warn("resourcePath=={}", resourcePath);
        log.warn("filePath==    {}", filePath);
        byte[] bytes = FileUtil.readBytes(filePath);

        // 创建HttpHeaders对象设置响应头信息
        MultiValueMap<String, String> headers = new HttpHeaders();
        // 设置要下载方式以及下载文件的名字
        headers.add("Content-Disposition", "attachment;filename=" + URLUtil.encode(fileName));

        // 设置响应状态码
        HttpStatus statusCode = HttpStatus.OK;

        // 创建ResponseEntity对象
        ResponseEntity<byte[]> responseEntity = new ResponseEntity(bytes, headers, statusCode);

        return responseEntity;
    }

    /**
     * 2、文件上传
     * 文件上传要求form表单的请求方式必须为post，并且添加属性enctype="multipart/form-data"
     * SpringMVC中将上传的文件封装到MultipartFile对象中，通过此对象可以获取文件相关信息
     * 上传步骤：
     * a>添加依赖：commons-fileupload
     * b>在SpringMVC的配置文件中添加配置：<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> </bean>
     * http://localhost:8080/lishuoboy_springmvc_war_exploded/a9/upload
     */
    @RequestMapping(value = "/upload")
    @ResponseBody
    public String fileUpload(@RequestParam(value = "file") MultipartFile multipartFile, HttpSession session) throws IOException {
        // <input>的name值
        String name = multipartFile.getName();
        log.warn("name=={}", name);
        //获取上传的文件的文件名
        String fileName = multipartFile.getOriginalFilename();
        log.warn("fileName=={}", fileName);
        // 处理文件重名问题
        String suffixName = fileName.substring(fileName.lastIndexOf("."));

        fileName = UUID.randomUUID() + suffixName;
        //获取服务器中photo目录的路径
        ServletContext servletContext = session.getServletContext();
        String filePath = servletContext.getRealPath("file");

        File file = new File(filePath);
        FileUtil.mkdir(file);
        String finalPath = filePath + File.separator + fileName;
        log.warn("finalPath=={}", finalPath);
        // 实现上传功能
        multipartFile.transferTo(new File(finalPath));
        return "success";
    }

    /**
     * 3、多文件同时上传(更多注释说明查看 2、文件上传 )
     * http://localhost:8080/lishuoboy_springmvc_war_exploded/a9/upload2
     */
    @RequestMapping(value = "/upload2")
    @ResponseBody
    public String fileUpload(@RequestParam(value = "file") MultipartFile multipartFile, MultipartFile[] fileArr, HttpSession session) throws IOException {

        //获取服务器中photo目录的路径
        String filePath = session.getServletContext().getRealPath("file");
        FileUtil.mkdir(new File(filePath));

        // 多文件保存
        for (int i = 0; i < fileArr.length; i++) {
            //获取上传的文件的文件名
            String fileName = fileArr[i].getOriginalFilename();

            String finalPath = filePath + File.separator + fileName;
            log.warn("finalPath=={}", finalPath);
            // 实现上传功能
            fileArr[i].transferTo(new File(finalPath));
        }

        // 单文件保存
        String finalPath = filePath + File.separator + multipartFile.getOriginalFilename();
        log.warn("finalPath=={}", finalPath);
        multipartFile.transferTo(new File(finalPath));

        return "success";
    }

    /**
     * 4、原生servlet单个、多文件同时上传
     * 【注意】测试时需注释掉springMVC中配置的 "multipartResolver"，否则文件被截流了
     * 所有文件内容（包括文件名字，文件类型，文件流）混合在一起了。需要自己截取，可以借助 commons-fileupload 依赖代替自己截取
     * <p>
     * http://localhost:8080/lishuoboy_springmvc_war_exploded/a9/upload3
     */
    @SneakyThrows
    @RequestMapping(value = "/upload3")
    @ResponseBody
    public String fileUpload(HttpServletRequest request, HttpSession session) {
        //获取服务器中photo目录的路径
        String filePath = session.getServletContext().getRealPath("file");
        FileUtil.mkdir(new File(filePath));

        log.warn("所有文件内容（包括文件名字，文件类型，文件流）request.getInputStream()=={}", IoUtil.read(request.getInputStream(), CharsetUtil.CHARSET_UTF_8));

        return "success";
    }

    /**
     * 5、原生servlet单个、多文件同时上传，借助commons-fileupload【尚硅谷教程里没有】
     * 【注意】测试时需注释掉springMVC中配置的 "multipartResolver"，否则文件被截流了
     * 所有文件内容（包括文件名字，文件类型，文件流）混合在一起了。需要自己截取，可以借助 commons-fileupload 依赖代替自己截取
     * <p>
     * http://localhost:8080/lishuoboy_springmvc_war_exploded/a9/upload4
     */
    @SneakyThrows
    @RequestMapping(value = "/upload4")
    @ResponseBody
    public String fileUpload4(HttpServletRequest request, HttpSession session) {
        //获取服务器中photo目录的路径
        String filePath = session.getServletContext().getRealPath("file");
        FileUtil.mkdir(new File(filePath));

        // 创建工具类 servletFileUpload
        ServletFileUpload servletFileUpload = new ServletFileUpload(new DiskFileItemFactory());

        // 判断是否有 type="file" 的input文件
        boolean isMultipartContent = ServletFileUpload.isMultipartContent(request);
        log.warn("isMultipartContent=={}", isMultipartContent);
        if (!isMultipartContent) {
            return "success";
        }

        List<FileItem> fileItems = servletFileUpload.parseRequest(request);
        for (int i = 0; i < fileItems.size(); i++) {
            FileItem fileItem = fileItems.get(i);
            log.warn("i=={}", i);
            log.warn("isFormField()=={}", fileItem.isFormField());
            log.warn("isInMemory()=={}", fileItem.isInMemory());
            log.warn("getName()=={}", fileItem.getName());  // 文件名
            log.warn("getFieldName()=={}", fileItem.getFieldName()); // input 的name值
            log.warn("getContentType()=={}", fileItem.getContentType());
//            log.warn("getString()=={}", fileItem.getString("UTF-8"));
//            log.warn("getInputStream()=={}", fileItem.getInputStream());
            if (!fileItem.isFormField() && StrUtil.isAllNotBlank(fileItem.getName())) {
                File finalPath = FileUtil.file(filePath + File.separator + fileItem.getName());
                log.warn("finalPath=={}", finalPath);
//                fileItem.write(finalPath);  // 自带的不好用,用 FileUtil
                FileUtil.writeFromStream(fileItem.getInputStream(), finalPath);
                // 字符文件可以用下面的方式
//                FileUtil.writeString(fileItem.getString("UTF-8"), finalPath, "UTF-8");
            }
        }

        return "success";
    }
}